//
//  ZLPullMenu.swift
//  ZLPullMenuSwift
//
//  Created by 科大 on 2025/1/16.
//

import UIKit

/// 菜单布局枚举
public enum ZLPullMenuLayout: Int {
    case `default`    /// 线性布局
    case center       /// 居中
}

/// 菜单代理协议
public protocol ZLPullMenuDelegate: NSObjectProtocol {
    /// 即将选中
    /// - Parameter indexPath: 下标
    func pullMenuWillClick(_ indexPath: ZLIndexPath) -> Bool
    
    /// 已经选中
    /// - Parameter indexPath: 下标
    func pullMenuDidClick(_ indexPath: ZLIndexPath)
}

public class ZLPullMenu: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, ZLPullMenuContentViewDelegate {
    // MARK: - Properties
    
    /// 列表
    private lazy var collectionView: UICollectionView = {
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
        cv.showsHorizontalScrollIndicator = false
        cv.register(ZLPullMenuCell.self, forCellWithReuseIdentifier: "ZLPullMenuCell")
        cv.delegate = self
        cv.dataSource = self
        return cv
    }()
    
    /// 布局
    private lazy var layout: UICollectionViewFlowLayout = {
        let layout = UICollectionViewFlowLayout()
        layout.minimumLineSpacing = 0
        layout.scrollDirection = .horizontal
        return layout
    }()
    
    /// 显示菜单
    private lazy var menuView: ZLPullMenuContentView = {
        let view = ZLPullMenuContentView(frame: CGRect(x: 0, y: frame.height, width: frame.width, height: 0))
        view.layer.masksToBounds = true
        view.delegate = self
        return view
    }()
    
    /// 选中菜单下标
    private var selectedIndex: Int = -1
    
    /// 背景
    private lazy var bottomView: UIView = {
        let view = UIView()
        view.backgroundColor = UIColor(white: 0, alpha: 0.2)
        view.alpha = 0
        let tap = UITapGestureRecognizer(target: self, action: #selector(closeAction))
        view.addGestureRecognizer(tap)
        return view
    }()
    
    /// 背景
    private lazy var topView: UIView = {
        let view = UIView()
        view.backgroundColor = .clear
        let tap = UITapGestureRecognizer(target: self, action: #selector(closeAction))
        view.addGestureRecognizer(tap)
        return view
    }()
    
    /// 代理
    public weak var delegate: ZLPullMenuDelegate?
    
    /// 数据源
    public var menus: [ZLPullMenuItem] = []
    
    /// 缩进
    public var insets: UIEdgeInsets = .zero {
        didSet {
            collectionView.contentInset = insets
        }
    }
    
    /// 间隔
    public var interval: CGFloat = 0 {
        didSet {
            layout.minimumLineSpacing = interval
            collectionView.reloadSections(IndexSet(integer: 0))
        }
    }
    
    /// 布局
    public var layoutMode: ZLPullMenuLayout = .default
    
    // MARK: - Initialization
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(collectionView)
        collectionView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Public Methods
    
    func reloadData() {
        collectionView.reloadData()
        for item in menus where item.selected {
            menuView.reloadData(item.groups)
            break
        }
    }
    
    // MARK: - Private Methods
    
    @objc private func closeAction() {
        menus.forEach { $0.selected = false }
        closeMenu()
    }
    
    private func openMenu() {
        collectionView.reloadData()
        guard let window = UIApplication.shared.windows.last else { return }
        
        let rect = convert(bounds, to: window)
        bottomView.frame = CGRect(x: 0, y: rect.maxY, width: window.frame.width, height: window.frame.height - rect.maxY)
        topView.frame = CGRect(x: 0, y: 0, width: window.frame.width, height: rect.minY)
        menuView.frame = CGRect(x: 0, y: rect.maxY, width: window.frame.width, height: 0)
        
        window.addSubview(bottomView)
        window.addSubview(topView)
        window.addSubview(menuView)
        
        UIView.animate(withDuration: 0.1) {
            self.bottomView.alpha = 1
            let maxHeight = min(window.frame.height - rect.maxY, self.menuView.getMenuHeight())
            self.menuView.frame = CGRect(x: 0, y: rect.maxY, width: window.frame.width, height: maxHeight)
        }
    }
    
    func closeMenu() {
        bottomView.removeFromSuperview()
        topView.removeFromSuperview()
        collectionView.reloadData()
        
        UIView.animate(withDuration: 0.1, animations: {
            self.bottomView.alpha = 0
            self.menuView.frame = CGRect(x: 0, y: self.menuView.frame.minY, width: self.menuView.frame.width, height: 0)
        }) { _ in
            self.menuView.removeFromSuperview()
        }
    }
    
    // MARK: - ZLPullMenuContentViewDelegate
    
    func pullMenuContentViewWillSelected(_ indexPath: IndexPath) -> Bool {
        let zindexPath = ZLIndexPath(menuIndex: selectedIndex, groupIndex: indexPath.section, optionIndex: indexPath.row)
        return delegate?.pullMenuWillClick(zindexPath) ?? true
    }
    
    func pullMenuContentViewDidSelected(_ indexPath: IndexPath) {
        let zindexPath = ZLIndexPath(menuIndex: selectedIndex, groupIndex: indexPath.section, optionIndex: indexPath.row)
        delegate?.pullMenuDidClick(zindexPath)
    }
    
    // MARK: - UICollectionViewDataSource & UICollectionViewDelegate
    
    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return menus.count
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let item = menus[indexPath.row]
        
        switch layoutMode {
        case .default:
            let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 14)]
            let width = (item.menuTitle as NSString).boundingRect(with: CGSize(width: 1000, height: 20),
                                                                options: .usesLineFragmentOrigin,
                                                                attributes: attributes,
                                                                context: nil).width
            collectionView.contentInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
            layout.minimumLineSpacing = 10
            return CGSize(width: width + 15, height: frame.height)
            
        case .center:
            let width = frame.width / CGFloat(menus.count)
            collectionView.contentInset = .zero
            layout.minimumLineSpacing = 0
            return CGSize(width: width, height: frame.height)
        }
    }
    
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ZLPullMenuCell", for: indexPath) as! ZLPullMenuCell
        cell.item = menus[indexPath.row]
        return cell
    }
    
    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let tapItem = menus[indexPath.row]
        menuView.reloadData(tapItem.groups)
        selectedIndex = -1
        
        for item in menus {
            if item !== tapItem {
                item.selected = false
                continue
            }
            if item.selected {
                item.selected = false
                closeMenu()
            } else {
                item.selected = true
                selectedIndex = indexPath.row
                openMenu()
            }
        }
    }
}

/// 选中方法
public extension ZLPullMenu {
    func item(with indexPath: ZLIndexPath) -> ZLPullMenuItem {
        return self.menus[indexPath.menuIndex]
    }
    func group(with indexPath: ZLIndexPath) -> ZLPullMenuGroup {
        return self.menus[indexPath.menuIndex].groups[indexPath.groupIndex]
    }
    func option(with indexPath: ZLIndexPath) -> ZLPullMenuOption {
        return self.menus[indexPath.menuIndex].groups[indexPath.groupIndex].options[indexPath.optionIndex]
    }
}
